package com.quectel.mobile.controller.partybuilding;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ReflectUtil;
import com.google.common.base.Preconditions;
import com.quectel.constant.core.partyeducationexam.PartyEducationExamConstants;
import com.quectel.constant.core.partyeducationexam.PartyEducationExamPersonConstants;
import com.quectel.constant.core.partyeducationexam.PartyEducationQuestionConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.partybuilding.dto.PartyPersonDto;
import com.quectel.core.module.partyeducationexam.dto.*;
import com.quectel.core.module.partyeducationexam.dto.ref.ExamRefPersonDto;
import com.quectel.core.module.partyeducationexam.dto.ref.QuestionRefExamDto;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamPersonHistoryService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamPersonService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamQuestionService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamService;
import com.quectel.core.module.partyeducationtraining.service.*;
import com.quectel.mobile.controller.BaseController;
import com.quectel.mobile.util.SessionHolder;
import com.quectel.mobile.vo.partyexam.GradeResp;
import com.quectel.mobile.vo.partyexam.SubmitHistoryResp;
import com.quectel.mobile.vo.partyexam.SubmitReq;
import com.quectel.util.common.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rananxin
 * @email ryan.ran@quectel.com
 * @date 2022/5/23 17:31
 */
@RestController
@RequestMapping("partyBuildingExam")
@Tag(name = "PartyBuildingExamController", description = "党员培训考试相关的api")
public class PartyBuildingExamController extends BaseController {

    @DubboReference
    private TrainingService trainingService;
    @DubboReference
    private TrainingPersonService trainingPersonService;
    @DubboReference
    private TrainingItemService trainingItemService;
    @DubboReference
    private TrainingItemPersonService trainingItemPersonService;
    @DubboReference
    private TrainingCommentService trainingCommentService;
    @DubboReference
    private TrainingExperienceService trainingExperienceService;
    @DubboReference
    private PartyEducationTrainingExamPersonService partyEducationTrainingExamPersonService;
    @DubboReference
    private PartyEducationTrainingExamPersonService examPersonService;
    @DubboReference
    private PartyEducationTrainingExamQuestionService examQuestionService;
    @DubboReference
    private PartyEducationTrainingExamService partyEducationTrainingExamService;
    @DubboReference
    private PartyEducationTrainingExamPersonHistoryService partyEducationTrainingExamPersonHistoryService;

    @GetMapping("list")
    @Operation(summary = "考试列表")
    public Response<Page<ExamRefPersonDto>> trainingList(
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize,
            @Parameter(description = "状态 1待参加 2已参加") @RequestParam(required = false) Byte status
    ) {
        // query training by person
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = SessionHolder.getLoginMobileUserSession().getPartyPerson();

        Request request = Request.configParams(Request.parseParams(httpServletRequest))
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getPartyPersonId), partyPerson.getId())
                .put(SystemConstants.STATUSES, new Byte[]{PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus()
                        , PartyEducationExamConstants.StatusEnum.CANCEL.getStatus()});

        if (status != null) {
            Map<String, Object> params = request.getParams();
            params.remove("status");
            //已参加就是已经参加过考试的
            if (status.equals((byte) 1)) {
                // 未参加且未结束
                params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getTakeStatus), PartyEducationExamPersonConstants.TakeStatusEnum.UNEXAMINED.getStatus());
                params.put("notEnded", SystemConstants.COMMON.toString());
            } else {
                params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getTakeStatus),PartyEducationExamPersonConstants.TakeStatusEnum.EXAMINED.getStatus());
            }
        }

        int total = examPersonService.queryExamTotal(request.getParams());
        List<ExamRefPersonDto> result = null;
        if (total > 0) {
            result = examPersonService.queryExamList(request.getParams());
//            result.forEach(examRefPersonDto -> {
//
//                PartyEducationTrainingExamPersonDto partyEducationTrainingExamPersonDto = partyEducationTrainingExamPersonService.selectOne(examRefPersonDto.getId(), examRefPersonDto.getPersonId());
//                if(partyEducationTrainingExamPersonDto!=null){
//                    examRefPersonDto.setStatus(partyEducationTrainingExamPersonDto.getStatus());
//                }
//                Map<String, Object> params = new HashMap<>();
//                params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonHistoryDto::getExamId), examRefPersonDto.getId());
//                params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonHistoryDto::getPartyPersonId), examRefPersonDto.getPersonId());
//                List<PartyEducationTrainingExamPersonHistoryDto> partyEducationTrainingExamPersonHistoryDtos = partyEducationTrainingExamPersonHistoryService.queryListWithoutPadding(params);
//
//                if(CollectionUtils.isNotEmpty(partyEducationTrainingExamPersonHistoryDtos)){
//                    PartyEducationTrainingExamPersonHistoryDto lastHistory = partyEducationTrainingExamPersonHistoryDtos.get(0);
//                    examRefPersonDto.setLastExamEndTime(lastHistory.getEndTime());
//                    examRefPersonDto.setLastExamStartTime(lastHistory.getStartTime());
//                    examRefPersonDto.setLastScore(lastHistory.getScore());
//
//                    PartyEducationTrainingExamPersonHistoryDto max = partyEducationTrainingExamPersonHistoryDtos.stream().max(Comparator.comparing(PartyEducationTrainingExamPersonHistoryDto::getScore)).orElse(null);
//                    if(max!=null){
//                        examRefPersonDto.setMaxScore(max.getScore());
//                    }
//                }
//
//            });
        } else {
            result = new ArrayList<>();
        }

        Page<ExamRefPersonDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, result);
        return Response.<Page<ExamRefPersonDto>>ok().wrap(page);
    }

    /**
     * 考试详情
     *
     * @param id
     * @return
     */
    @GetMapping("examDetail/{id}")
    @Operation(summary = "考试详情")
    public Response<ExamRefPersonDto> examDetail(@PathVariable("id") Long id) {

        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = SessionHolder.getLoginMobileUserSession().getPartyPerson();

        Request request = Request.configParams(Request.parseParams(httpServletRequest))
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getPartyPersonId), partyPerson.getId())
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamDto::getStatus), PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus())
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getExamId), id);

        List<ExamRefPersonDto> examRefPersonDtos = partyEducationTrainingExamPersonService.queryExamList(request.getParams());
        if (CollectionUtils.isEmpty(examRefPersonDtos)) {
            return Response.error("考试不存在");
        }
        ExamRefPersonDto examRefPersonDto = examRefPersonDtos.get(0);

        if (StringUtils.isNotBlank(examRefPersonDto.getQuestionSetting())) {
            ExamQuestionSettingCls parse = JacksonUtils.parse(examRefPersonDto.getQuestionSetting(), ExamQuestionSettingCls.class);
            int qc = 0;
            Field[] fields = ReflectUtil.getFields(ExamQuestionSettingCls.class);
            for (Field field : fields) {
                Object fieldValue = ReflectUtil.getFieldValue(parse, field);
                if (fieldValue != null) {
                    qc += (Integer)ReflectUtil.getFieldValue(fieldValue, "number");
                }
            }
            examRefPersonDto.setQuestionCount(qc);
        }

        return Response.<ExamRefPersonDto>ok().wrap(examRefPersonDto);
    }

    /**
     * 开始考试
     *
     * @param id
     * @return
     */
    @GetMapping("start/{id}")
    @Operation(summary = "开始考试")
    public Response<ExamRefPersonDto> start(@PathVariable("id") Long id) {

        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = SessionHolder.getLoginMobileUserSession().getPartyPerson();

        Request request = Request.configParams(Request.parseParams(httpServletRequest))
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getPartyPersonId), partyPerson.getId())
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamDto::getStatus), PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus())
                .put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonDto::getExamId), id);

        List<ExamRefPersonDto> examRefPersonDtos = partyEducationTrainingExamPersonService.queryExamList(request.getParams());
        if (CollectionUtils.isEmpty(examRefPersonDtos)) {
            return Response.error("考试不存在");
        }
        ExamRefPersonDto examRefPersonDto = examRefPersonDtos.get(0);
        if (examRefPersonDto.getAllowTime() != 0
                && examRefPersonDto.getTakeCount() >= examRefPersonDto.getAllowTime()) {
            return Response.error("参加考试次数过多，不允许参加");
        }
        if (!PartyEducationExamConstants.SpecificStatusEnum.IN_PROGRESS.getStatus().equals(examRefPersonDto.getSpecificStatus())) {
            return Response.error("考试不在进行中");
        }

        ExamTestStartRespDto examTestStartRespDto = partyEducationTrainingExamService.start(ExamTestStartReqDto.builder()
                .examId(id)
                .personId(partyPerson.getId())
                .build());

        if (Objects.isNull(examTestStartRespDto)) {
            return Response.error("开始考试失败，请联系管理员");
        }

        examRefPersonDto.setQuestions(examTestStartRespDto.getExamQuestions());
        examRefPersonDto.setTestId(examTestStartRespDto.getTestId());
        return Response.<ExamRefPersonDto>ok().wrap(examRefPersonDto);
    }

    @GetMapping("check/{id}")
    @Operation(summary = "检查考试状态")
    public Response<PartyEducationTrainingExamDto> check(@PathVariable("id") Long id) {
        PartyEducationTrainingExamDto partyEducationTrainingExamDto = partyEducationTrainingExamService.selectCacheById(id);
        if (Objects.isNull(partyEducationTrainingExamDto)) {
            return Response.error("考试不存在");
        }
        return Response.<PartyEducationTrainingExamDto>ok().wrap(partyEducationTrainingExamDto);
    }

    @PostMapping("submit")
    @Operation(summary = "提交答卷")
    public Response<GradeResp> submit(@RequestBody @Validated SubmitReq submitReq) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();

        PartyEducationTrainingExamPersonHistoryDto partyEducationTrainingExamPersonHistoryDto = partyEducationTrainingExamPersonHistoryService.selectCacheById(submitReq.getTestId());
        if (Objects.nonNull(partyEducationTrainingExamPersonHistoryDto.getEndTime())) {
            return Response.error("测试已提交过");
        }
        Preconditions.checkNotNull(partyEducationTrainingExamPersonHistoryDto);

        PartyEducationTrainingExamDto partyEducationTrainingExamDto = partyEducationTrainingExamService.selectCacheById(partyEducationTrainingExamPersonHistoryDto.getExamId());
        Preconditions.checkNotNull(partyEducationTrainingExamDto);

        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(LambdaUtil.getFieldName(PartyEducationTrainingExamQuestionDto::getExamId), partyEducationTrainingExamPersonHistoryDto.getExamId());
        List<QuestionRefExamDto> questionRefExamDtos = examQuestionService.queryQuestionList(queryParams);

        List<EntrantAnswerCls> originAnswerList = JacksonUtils.parseArray(partyEducationTrainingExamPersonHistoryDto.getAnswer(), EntrantAnswerCls.class);
        Map<Long, EntrantAnswerCls> answerMap = new HashMap<>();
        for (EntrantAnswerCls entrantAnswerCls : originAnswerList) {
            answerMap.put(entrantAnswerCls.getQuestionId(), entrantAnswerCls);
        }

        if (PartyEducationExamConstants.SelectModeEnum.RANDOM.getMode().equals(partyEducationTrainingExamDto.getSelectMode())) {
            // 随机选题，过滤出放出的题目
            questionRefExamDtos = questionRefExamDtos.stream().filter(o -> answerMap.containsKey(o.getId())).collect(Collectors.toList());
        }

        for (QuestionRefExamDto questionRefExamDto : questionRefExamDtos) {
            EntrantAnswerCls entrantAnswerCls = answerMap.get(questionRefExamDto.getId());
            if (Objects.nonNull(entrantAnswerCls)) {
                questionRefExamDto.setSeq(entrantAnswerCls.getSeq());
            }
        }

        grade(questionRefExamDtos, submitReq.getAnswerList(), partyEducationTrainingExamDto.getGradeMode());
        List<PartyEducationQuestionDto> gradeQuestions = CopyUtils.copyList(questionRefExamDtos, PartyEducationQuestionDto.class);

        ExamSubmitDto examSubmitDto = new ExamSubmitDto();
        examSubmitDto.setExamId(partyEducationTrainingExamDto.getId());
        examSubmitDto.setTestId(partyEducationTrainingExamPersonHistoryDto.getId());
        examSubmitDto.setPersonId(partyPerson.getId());
        examSubmitDto.setQuestions(gradeQuestions);

        boolean existShortAnswerQuestion = questionRefExamDtos.stream()
                .anyMatch(o -> PartyEducationQuestionConstants.QuestionTypeEnum.SHORT_ANSWER.getType()
                        .equals(o.getType()));
        BigDecimal score = gradeQuestions.stream().map(PartyEducationQuestionDto::getEntrantScore).reduce(BigDecimal.ZERO, BigDecimal::add);
        Byte isPass = score.compareTo(new BigDecimal(partyEducationTrainingExamDto.getPassScore() + "")) >= 0 ? PartyEducationExamPersonConstants.PassStatusEnum.PASSED.getStatus()
                : PartyEducationExamPersonConstants.PassStatusEnum.FAIL.getStatus();
        // 是否有未自动审阅的主观题
        examSubmitDto.setScore(score);
        examSubmitDto.setPassStatus(existShortAnswerQuestion && PartyEducationExamConstants.GradeModeEnum.SEMI_AUTOMATIC.getGradeMode().equals(partyEducationTrainingExamDto.getGradeMode())
                ? PartyEducationExamPersonConstants.PassStatusEnum.UNRECORDED.getStatus() : isPass);
        examSubmitDto.setSubmitTime(new Date());
        partyEducationTrainingExamService.submit(examSubmitDto);

        int right = 0;
        int wrong = 0;
        for (PartyEducationQuestionDto gradeQuestion : gradeQuestions) {
            if (PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus().equals(gradeQuestion.getGrade())) {
                right++;
            } else if (PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus().equals(gradeQuestion.getGrade())) {
                wrong++;
            }
        }

        GradeResp gradeResp = new GradeResp();
        gradeResp.setTestId(partyEducationTrainingExamPersonHistoryDto.getId());
        gradeResp.setExamId(partyEducationTrainingExamDto.getId());
        gradeResp.setExamName(partyEducationTrainingExamDto.getName());
        gradeResp.setGradeStatus((byte) (existShortAnswerQuestion ? 0 : 1));
        gradeResp.setIsPass((byte) (score.compareTo(new BigDecimal(partyEducationTrainingExamDto.getPassScore().toString())) >= 0 ? 1 : 0));
        gradeResp.setTotalScore(partyEducationTrainingExamDto.getTotalScore());
        gradeResp.setPassScore(partyEducationTrainingExamDto.getPassScore());
        gradeResp.setScore(score);
        gradeResp.setQuestionTotal(questionRefExamDtos.size());
        gradeResp.setQuestionRight(right);
        gradeResp.setQuestionWrong(wrong);
        if (!PartyEducationExamConstants.AnswerShowMomentEnum.HAND_PAPER.getType().equals(partyEducationTrainingExamDto.getAnswerShowMoment())
                && !PartyEducationExamConstants.SpecificStatusEnum.ENDING.getStatus().equals(partyEducationTrainingExamDto.getSpecificStatus())) {
            // 答案显示时机，不是交卷立即且考试未结束，隐藏答案
            for (QuestionRefExamDto questionRefExamDto : questionRefExamDtos) {
                questionRefExamDto.setAnswer(null);
                questionRefExamDto.setAnalysis(null);
            }
        }
        questionRefExamDtos.sort(Comparator.comparing(QuestionRefExamDto::getSeq));
        gradeResp.setQuestions(questionRefExamDtos);
        return Response.<GradeResp>ok().wrap(gradeResp);
    }

    @GetMapping("history/{id}")
    @Operation(summary = "查看提交历史")
    public Response<List<SubmitHistoryResp>> history(@PathVariable("id") Long id) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = SessionHolder.getLoginMobileUserSession().getPartyPerson();

        Map<String, Object> params = new HashMap<>();
        params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonHistoryDto::getExamId), id);
        params.put(LambdaUtil.getFieldName(PartyEducationTrainingExamPersonHistoryDto::getPartyPersonId), partyPerson.getId());
        List<PartyEducationTrainingExamPersonHistoryDto> partyEducationTrainingExamPersonHistoryDtos = partyEducationTrainingExamPersonHistoryService.queryList(params);
        List<SubmitHistoryResp> submitHistoryResps = CopyUtils.copyList(partyEducationTrainingExamPersonHistoryDtos, SubmitHistoryResp.class)
                .stream().filter(o -> o.getEndTime() != null)
                .sorted((a,b)-> DateUtil.compare(a.getEndTime(), b.getEndTime()))
                .collect(Collectors.toList());

        SubmitHistoryResp maxOne = submitHistoryResps.stream().max(Comparator.comparing(SubmitHistoryResp::getScore)).orElse(null);
        for (SubmitHistoryResp submitHistoryResp : submitHistoryResps) {
            if (Objects.nonNull(maxOne) && maxOne.getScore().compareTo(submitHistoryResp.getScore()) == 0) {
                submitHistoryResp.setIsMax((byte) 1);
                continue;
            }
            submitHistoryResp.setIsMax((byte) 0);
        }
        return Response.<List<SubmitHistoryResp>>ok().wrap(submitHistoryResps);
    }


    @GetMapping("lastHistory/{testId}")
    @Operation(summary = "答卷详情")
    public Response<GradeResp> lastHistory(@PathVariable("testId") Long testId) {
        SessionHolder.checkPartyPerson();

        PartyEducationTrainingExamPersonHistoryDto partyEducationTrainingExamPersonHistoryDto = partyEducationTrainingExamPersonHistoryService.selectCacheById(testId);
        if (Objects.isNull(partyEducationTrainingExamPersonHistoryDto)) {
            return Response.error("考试测试不存在");
        }
        if (Objects.isNull(partyEducationTrainingExamPersonHistoryDto.getEndTime())) {
            return Response.error("考试测试未结束");
        }

        PartyEducationTrainingExamDto partyEducationTrainingExamDto = partyEducationTrainingExamService.selectCacheById(partyEducationTrainingExamPersonHistoryDto.getExamId());
        Preconditions.checkNotNull(partyEducationTrainingExamDto);

        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(LambdaUtil.getFieldName(PartyEducationTrainingExamQuestionDto::getExamId), partyEducationTrainingExamPersonHistoryDto.getExamId());
        List<QuestionRefExamDto> questionRefExamDtos = examQuestionService.queryQuestionList(queryParams);

        List<EntrantAnswerCls> originAnswerList = JacksonUtils.parseArray(partyEducationTrainingExamPersonHistoryDto.getAnswer(), EntrantAnswerCls.class);
        Map<Long, EntrantAnswerCls> answerMap = new HashMap<>();
        for (EntrantAnswerCls entrantAnswerCls : originAnswerList) {
            answerMap.put(entrantAnswerCls.getQuestionId(), entrantAnswerCls);
        }

        if (PartyEducationExamConstants.SelectModeEnum.RANDOM.getMode().equals(partyEducationTrainingExamDto.getSelectMode())) {
            // 随机选题，过滤出放出的题目
            questionRefExamDtos = questionRefExamDtos.stream().filter(o -> answerMap.containsKey(o.getId())).collect(Collectors.toList());
        }

        for (QuestionRefExamDto questionRefExamDto : questionRefExamDtos) {
            EntrantAnswerCls entrantAnswerCls = answerMap.get(questionRefExamDto.getId());
            if (Objects.nonNull(entrantAnswerCls)) {
                questionRefExamDto.setSeq(entrantAnswerCls.getSeq());
                questionRefExamDto.setEntrantAnswer(JacksonUtils.toJsonString(entrantAnswerCls.getEntrantAnswer()));
                questionRefExamDto.setEntrantScore(entrantAnswerCls.getScore());
                questionRefExamDto.setGrade(entrantAnswerCls.getGrade());
            }
        }

        int right = 0;
        int wrong = 0;
        for (PartyEducationQuestionDto gradeQuestion : questionRefExamDtos) {
            if (PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus().equals(gradeQuestion.getGrade())) {
                right++;
            } else if (PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus().equals(gradeQuestion.getGrade())) {
                wrong++;
            }
        }

        GradeResp gradeResp = new GradeResp();
        gradeResp.setTestId(partyEducationTrainingExamPersonHistoryDto.getId());
        gradeResp.setExamId(partyEducationTrainingExamDto.getId());
        gradeResp.setExamName(partyEducationTrainingExamDto.getName());
        gradeResp.setGradeStatus((byte)(PartyEducationExamPersonConstants.PassStatusEnum.UNRECORDED.getStatus().equals(partyEducationTrainingExamPersonHistoryDto.getPassStatus()) ? 0 : 1));
        gradeResp.setIsPass((byte) (partyEducationTrainingExamPersonHistoryDto.getScore().compareTo(new BigDecimal(partyEducationTrainingExamDto.getPassScore().toString())) >= 0 ? 1 : 0));
        gradeResp.setTotalScore(partyEducationTrainingExamDto.getTotalScore());
        gradeResp.setPassScore(partyEducationTrainingExamDto.getPassScore());
        gradeResp.setScore(partyEducationTrainingExamPersonHistoryDto.getScore());
        gradeResp.setQuestionTotal(questionRefExamDtos.size());
        gradeResp.setQuestionRight(right);
        gradeResp.setQuestionWrong(wrong);
        if (!PartyEducationExamConstants.AnswerShowMomentEnum.HAND_PAPER.getType().equals(partyEducationTrainingExamDto.getAnswerShowMoment())
                && !PartyEducationExamConstants.SpecificStatusEnum.ENDING.getStatus().equals(partyEducationTrainingExamDto.getSpecificStatus())) {
            // 答案显示时机，不是交卷立即且考试未结束，隐藏答案
            for (QuestionRefExamDto questionRefExamDto : questionRefExamDtos) {
                questionRefExamDto.setAnswer(null);
                questionRefExamDto.setAnalysis(null);
            }
        }
        questionRefExamDtos.sort(Comparator.comparing(QuestionRefExamDto::getSeq));
        gradeResp.setQuestions(questionRefExamDtos);
        return Response.<GradeResp>ok().wrap(gradeResp);
    }

    @GetMapping("wrongList")
    @Operation(summary = "错题集")
    public Response<List<QuestionRefExamDto>> wrongList(@Parameter(description = "考试测试 id") @RequestParam Long testId) {
        SessionHolder.checkPartyPerson();

        PartyEducationTrainingExamPersonHistoryDto partyEducationTrainingExamPersonHistoryDto = partyEducationTrainingExamPersonHistoryService.selectCacheById(testId);
        if (Objects.isNull(partyEducationTrainingExamPersonHistoryDto)) {
            return Response.error("考试测试不存在");
        }
        if (Objects.isNull(partyEducationTrainingExamPersonHistoryDto.getEndTime())) {
            return Response.error("考试测试未结束");
        }

        PartyEducationTrainingExamDto partyEducationTrainingExamDto = partyEducationTrainingExamService.selectCacheById(partyEducationTrainingExamPersonHistoryDto.getExamId());
        Preconditions.checkNotNull(partyEducationTrainingExamDto);

        List<EntrantAnswerCls> entrantAnswerList = JacksonUtils.parseArray(partyEducationTrainingExamPersonHistoryDto.getAnswer(), EntrantAnswerCls.class);
        List<EntrantAnswerCls> wrongAnswerList = entrantAnswerList.stream().filter(o -> o.getGrade().equals(PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus())).collect(Collectors.toList());
        Map<Long, EntrantAnswerCls> map = new HashMap<>();
        for (EntrantAnswerCls entrantAnswerCls : wrongAnswerList) {
            map.put(entrantAnswerCls.getQuestionId(), entrantAnswerCls);
        }

        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(LambdaUtil.getFieldName(PartyEducationTrainingExamQuestionDto::getExamId), partyEducationTrainingExamPersonHistoryDto.getExamId());
        List<QuestionRefExamDto> questionRefExamDtos = examQuestionService.queryQuestionList(queryParams);
        questionRefExamDtos.sort(Comparator.comparing(QuestionRefExamDto::getType));
        List<QuestionRefExamDto> result = questionRefExamDtos.parallelStream().filter(o -> map.containsKey(o.getId())).collect(Collectors.toList());
        for (QuestionRefExamDto questionRefExamDto : result) {
            EntrantAnswerCls entrantAnswerCls = map.get(questionRefExamDto.getId());
            if (Objects.nonNull(entrantAnswerCls)) {
                questionRefExamDto.setSeq(entrantAnswerCls.getSeq());
                questionRefExamDto.setEntrantAnswer(JacksonUtils.toJsonString(entrantAnswerCls.getEntrantAnswer()));
                questionRefExamDto.setEntrantScore(entrantAnswerCls.getScore());
                questionRefExamDto.setGrade(entrantAnswerCls.getGrade());
            }

            if (!PartyEducationExamConstants.AnswerShowMomentEnum.HAND_PAPER.getType().equals(partyEducationTrainingExamDto.getAnswerShowMoment())
                    && !PartyEducationExamConstants.SpecificStatusEnum.ENDING.getStatus().equals(partyEducationTrainingExamDto.getSpecificStatus())) {
                // 答案显示时机，不是交卷立即且考试未结束，隐藏答案
                questionRefExamDto.setAnswer(null);
                questionRefExamDto.setAnalysis(null);
            }
        }
        return Response.<List<QuestionRefExamDto>>ok().wrap(result);
    }

    private void grade(List<QuestionRefExamDto> questions, List<SubmitReq.AnswerCls> answerList, Byte gradeMode) {
        Map<Long, String[]> answerMap = new HashMap<>();
        for (SubmitReq.AnswerCls answerCls : answerList) {
            answerMap.put(answerCls.getQuestionId(), answerCls.getAnswer());
        }

        for (QuestionRefExamDto question : questions) {
            Byte isRight = PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus();
            String[] answer = answerMap.get(question.getId());
            if (Objects.nonNull(answer)) {
                question.setEntrantAnswer(JacksonUtils.toJsonString(answer));
                String rightAnswer = question.getAnswer();
                if (PartyEducationQuestionConstants.NeedOrderEnum.NEED.getOrderStatus().equals(question.getInOrder())
                        && rightAnswer.equals(JacksonUtils.toJsonString(answer))) {
                    isRight = PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus();
                } else {
                    if (PartyEducationQuestionConstants.NeedOrderEnum.NOT_NEED.getOrderStatus().equals(question.getInOrder())) {
                        List<String> rightAnswerList = JacksonUtils.parseArray(rightAnswer, String.class);
                        for (int i = 0; i < rightAnswerList.size(); i++) {
                            String a = rightAnswerList.get(i);
                            if (!Arrays.asList(answer).contains(a)) {
                                break;
                            }
                            if (i == rightAnswerList.size() - 1) {
                                isRight = PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus();
                            }
                        }

                        // 多选题情况，未全部勾选，给部分分值
                        if (PartyEducationQuestionConstants.QuestionTypeEnum.MULTIPLE_CHOICE.getType().equals(question.getType())
                                && PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus().equals(isRight)) {
                            for (int i = 0; i < answer.length; i++) {
                                if (!rightAnswerList.contains(answer[i])) {
                                    break;
                                }
                                if (i == answer.length - 1) {
                                    isRight = PartyEducationQuestionConstants.GradeEnum.WRONG.getStatus();
                                    question.setEntrantScore(question.getMissScore());
                                }
                            }
                        }

                        // 全自动阅卷，主观题根据关键字给分
                        if (PartyEducationExamConstants.GradeModeEnum.AUTOMATIC.getGradeMode().equals(gradeMode)
                                && PartyEducationQuestionConstants.QuestionTypeEnum.SHORT_ANSWER.getType().equals(question.getType())
                                && StringUtils.isNotBlank(question.getKeywords())) {
                            List<String> keyWords = JacksonUtils.parseArray(question.getKeywords(), String.class);
                            int cc = 0;
                            for (String keyWord : keyWords) {
                                if (JacksonUtils.toJsonString(answer).contains(keyWord)) {
                                    cc++;
                                }
                            }
                            if (cc != 0 && CollectionUtils.isNotEmpty(keyWords)) {
                                BigDecimal score = new BigDecimal(String.valueOf(cc))
                                        .subtract(question.getScore())
                                        .divide(new BigDecimal(String.valueOf(keyWords.size())), 1, RoundingMode.HALF_UP);
                                isRight = PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus();
                                question.setEntrantScore(score);
                            }
                        }

                    }
                }
            }
            question.setGrade(isRight);
            if (question.getEntrantScore() == null) {
                question.setEntrantScore(PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus().equals(isRight) ? question.getScore() : BigDecimal.ZERO);
            }
        }
    }

}
